#!/usr/bin/env python
# -*- coding: UTF-8 -*-

#-------------------------------------------------------------------------------
# Copyright (c) 2010 Giuseppe Buzzanca (PiDy) <giuseppebuzzanca@gmail.com>.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Contributors:
#     Giuseppe Buzzanca (PiDy) <giuseppebuzzanca@gmail.com>
#         initial API and implementation
#-------------------------------------------------------------------------------

from google.appengine.ext import webapp, db
import logging
import urllib
from google.appengine.api import urlfetch
from google.appengine.ext.webapp.util import run_wsgi_app
import sys
import oauth
from django.utils import simplejson
from properties import GET_BUZZ_USER_PROFILE, BUZZ_USER_PUBLIC_FEED, URL_PUBLISH, \
    URL_CALLBACK, URL_SUBSCRIBE, CONSUMER_KEY, CONSUMER_SECRET
from dbclasses import Users


def unsubscribeThis(self, id):
    """Return the status code of a urlfetch of /pshbconnect.
    Receive the current user id by id parameter and call a POST
    method to /pshbconnect with as parameters id and unsubscribe mode.
    """
    form_fields = {'hub.userid': id, 'hub.mode': 'unsubscribe'}
    form_data = urllib.urlencode(form_fields)
    result = urlfetch.fetch(url = self.request.host_url, payload = form_data, method = urlfetch.POST, headers = {'Content-Type': 'application/x-www-form-urlencoded'}, deadline = 10)
    return result.status_code


class PSHBConnect(webapp.RequestHandler):
    """Extend the webapp.RequestHandler.
    Class PSHBConnect is mapped on /pshbconnect request.
    """

    def post(self):
        """The method called when /pshbconnect is called with POST method.
        It performs subscribe and unsubscribe of the hub (see http://http://code.google.com/p/pubsubhubbub/).
        Receive userID and mode from POST call.
        Return the userID managed.
        """
        userId = self.request.get('hub.userid')
        hubMode = self.request.get('hub.mode')
        try:
            result = urlfetch.fetch(url = GET_BUZZ_USER_PROFILE.replace('userId', userId), deadline = 10)
        except urlfetch.DownloadError:
            logging.error("Error on retrieve Google User Profile informations.")
            self.response.set_status(500)
            self.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
            self.response.out.write("Error on retrieve Google User Profile informations.")
            return
        logging.debug("Received Google Profile Informations: " + str(result.content))
        try:
            content = simplejson.loads(result.content)['data']
            userId = content['id']
            hubUrl = BUZZ_USER_PUBLIC_FEED.replace('userId', userId)
        except:
            content = simplejson.loads(result.content)['error']
            logging.error("Error on parse Buzz User Profile retrieved")
            for error in content['errors']:
                for k, v in error.items():
                    logging.error(k + ' => ' + v)
            self.response.set_status(500)
            self.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
            self.response.out.write("Error on retrieve Google User Profile informations.")
            return
        logging.debug("Reconstructed hubUrl: " + hubUrl)
        if hubMode == 'subscribe':
            u = Users.get_by_key_name(userId)
            if u:
                logging.error("This user is already subscribed")
                self.response.set_status(500)
                self.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
                self.response.out.write("This user is already subscribed")
                return
            publish_fields = {'hub.mode': 'publish', 'hub.url': hubUrl}
            publish_data = urllib.urlencode(publish_fields)
            try:
                result = urlfetch.fetch(url = URL_PUBLISH, payload = publish_data, method = urlfetch.POST, deadline = 10)
                if result.status_code != 204:
                    raise urlfetch.DownloadError
            except urlfetch.DownloadError:
                logging.error('Error on set publisher')
                self.response.set_status(500)
                self.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
                self.response.out.write('Error on set publisher')
                return
        subscribe_fields = {'hub.callback': URL_CALLBACK, 'hub.mode': hubMode, 'hub.topic': hubUrl, 'hub.verify': 'async', 'hub.verify_token': userId}
        subscribe_data = urllib.urlencode(subscribe_fields)
        try:
            result = urlfetch.fetch(url = URL_SUBSCRIBE, payload = subscribe_data, method = urlfetch.POST, deadline = 10)
            if result.status_code != 202:
                raise urlfetch.DownloadError
        except urlfetch.DownloadError:
            logging.error('Error on set subscriber')
            if hubMode == "subscribe":
                unsubscribeThis(userId)
            self.response.set_status(500)
            self.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
            self.response.out.write('Error on set subscriber')
            return
        try:
            if hubMode == 'subscribe':
                u = Users(key_name = userId, topic = hubUrl, bitlyLogin = self.request.get("bitly.login").lower(), bitlyApiKey = self.request.get("bitly.apiKey"), active = True)
                u.put()
                logging.debug("User " + hubUrl + " inserted into Users DB with key_name: " + userId)
            elif hubMode == 'unsubscribe':
                r = Users.get_by_key_name(userId)
                db.delete(r)
                logging.debug("User " + userId + " deleted from Users DB")
        except:
            logging.error("Error management DB")
            self.response.set_status(500)
            self.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
            if hubMode == "subscribe":
                unsubscribeThis(userId)
                self.response.out.write("Error while store user information. Please, unsubscribe and try again.")
            else:
                self.response.out.write("Error while delete user information. Please, contact us for remove your information from our database.")
            return
        self.response.out.write(userId)

    def get(self, mode = None):
        """Wrapper method. If this class is called with GET method the request
        is forwarded to the post method.
        """
        self.post(mode)


class TwitterConnect(webapp.RequestHandler):
    """Extend the webapp.RequestHandler.
    Class TwitterConnect is mapped on /twitterconnect/(.*) request.
    """

    def post(self, mode = None):
        """Called when class is called woth POST method.
        Contact twitter host and request authorization url and redirect it.
        The authorization url callback this and user information are received and saved
        into Users table.
        Redirect to the root of web app with a connected variable in GET method,
        the variable contains a message related the result of the execution.
        """
        kName = self.request.get("kname")
        twitterCallbackUrl = "%s/twitterconnect/verify?kname=%s" % (self.request.host_url, kName)
        twitterClient = oauth.TwitterClient(CONSUMER_KEY, CONSUMER_SECRET, twitterCallbackUrl)
        if mode == "login":
            logging.debug("Contact twitter for authorization url")
            try:
                return self.redirect(twitterClient.get_authorization_url())
            except urlfetch.DownloadError:
                logging.error("Error when try to contact twiter.com for get authorization")
                logging.error(sys.exc_info())
                unsubscribeThis(kName)
                return self.redirect("/?connected=false&messagge=Error when try to contact twiter.com for get authorization. Please, try again later.")
        if mode == "verify":
            twitterAuthToken = self.request.get("oauth_token")
            twitterAuthVerifier = self.request.get("oauth_verifier")
            logging.debug("Contact twitter for user info")
            try:
                userInfo = twitterClient.get_user_info(twitterAuthToken, twitterAuthVerifier)
            except urlfetch.DownloadError:
                logging.error("Error when try to contact twiter.com for get user info. Please, unsubscribe and try again later.")
                logging.error(sys.exc_info())
                unsubscribeThis(kName)
                return self.redirect("/?connected=false&messagge=Error when try to contact twiter.com for get user info. Please, unsubscribe and try again later.")
            logging.debug(userInfo)
            try:
                u = Users.get_by_key_name(kName)
                u.twitterUserName = userInfo['username']
                u.twitterUserToken = userInfo["token"]
                u.twitterUserSecret = userInfo["secret"]
                db.put(u)
            except:
                logging.error("Error on store twitter user informations. Please, unsubscribe and try again.")
                logging.error(sys.exc_info())
                unsubscribeThis(kName)
                return self.redirect("/?connected=false&messagge=Error on store twitter user informations. Please, unsubscribe and try again.")
            logging.debug("Twitter information inserted into Users DB for current user")
            self.redirect("/?connected=true&messagge=User Feed correctly subscribed")

    def get(self, mode = None):
        """Wrapper method. If this class is called with GET method the request
        is forwarded to the post method.
        """
        self.post(mode)


application = webapp.WSGIApplication([('/twitterconnect/(.*)', TwitterConnect), ('/pshbconnect', PSHBConnect)], debug = True)


if __name__ == '__main__':
    run_wsgi_app(application)
